﻿#include "pch.h"
#include "Const.h"
#include "Server.h"

/**
 * 构造方法。
 */
Server::Server(): 
    mDatabase(NULL) {
    // 创建数据库
    this->CreateDatabase();

    // 初始化网络
    WSADATA wsaData = { 0 };
    WSAStartup(MAKEWORD(2, 2), &wsaData);
}

/**
 * 析构方法。
 */
Server::~Server() {
    // 关闭套接字
    CSocket::Close();

    // 关闭数据库
    sqlite3_close_v2(mDatabase);

    // 清空在线用户列表
    POSITION pos = mOnlineUsers.GetStartPosition();
    CString username;
    Client* client = NULL;
    while (pos) {
        mOnlineUsers.GetNextAssoc(pos, username, client);
        client->Close();
        delete client;
    }
    mOnlineUsers.RemoveAll();

    // 反初始化网络
    WSACleanup();
}

/**
 * 创建数据库。
 */
void Server::CreateDatabase() {
    sqlite3_stmt* query = NULL; // 数据库查询
    CString sql;                // 查询语句

    // 打开数据库
    sqlite3_open16(Const::DB_PATH, &mDatabase);

    // 创建表
    /* 用户表 */
    sql.Format(_T("CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY,%s TEXT,%s INTEGER)"),
        Const::TABLE_USERS, Const::FIELD_NAME, Const::FIELD_PASSWORD, Const::FIELD_ICON_INDEX);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    /* 好友表 */
    sql.Format(_T("CREATE TABLE IF NOT EXISTS %s(%s TEXT,%s TEXT)"),
        Const::TABLE_FRIENDS, Const::FIELD_OWNER, Const::FIELD_FRIEND_NAME);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    /* 离线聊天表 */
    sql.Format(_T("CREATE TABLE IF NOT EXISTS %s(%s TEXT,%s TEXT,%s TEXT,%s TEXT)"),
        Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, Const::FIELD_SRC_USER, Const::FIELD_CONTENT, Const::FIELD_TIME);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    /* 离线添加好友表 */
    sql.Format(_T("CREATE TABLE IF NOT EXISTS %s(%s TEXT,%s TEXT,%s INTEGER)"),
        Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, Const::FIELD_DST_USER, Const::FIELD_RESULT);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);
}

/**
 * 处理添加好友请求。
 * @param request   添加好友请求
 * @param client    客户端
 */
void Server::HandleAddFriendRequest(AddFriendRequest* request, Client* client) {
    sqlite3_stmt* query = NULL;
    CString sql;

    // 检查目标用户是否存在
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s'"), Const::TABLE_USERS, Const::FIELD_NAME, request->mDstUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    if (sqlite3_step(query) == SQLITE_DONE) {
        // 查无此人，通知源用户
        AddFriendResult result(0, request->mDstUsername, USER_NOT_FOUND, NULL);
        this->SendData(client, &result, sizeof(result));
        sqlite3_finalize(query);
        return;
    }
    sqlite3_finalize(query);

    // 检查是否已经是好友了
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s' AND %s='%s'"), Const::TABLE_FRIENDS, Const::FIELD_OWNER, request->mSrcUsername, Const::FIELD_FRIEND_NAME, request->mDstUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    if (sqlite3_step(query) != SQLITE_DONE) {
        // 已经是好友了，通知源用户
        AddFriendResult result(0, request->mDstUsername, ALREADY_BE_FRIENDS, NULL);
        this->SendData(client, &result, sizeof(result));
        return;
    }
    sqlite3_finalize(query);

    // 如果目标用户不在线，则将请求信息插入到数据库中
    Client* friendUser = NULL;
    BOOL result = mOnlineUsers.Lookup(request->mDstUsername, friendUser);
    if (!result) {
        sql.Format(_T("INSERT INTO %s(%s,%s) VALUES('%s','%s')"), 
            Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, Const::FIELD_DST_USER, request->mSrcUsername, request->mDstUsername);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
        sqlite3_step(query);
        sqlite3_finalize(query);
        return;
    }

    // 向目标用户发送请求
    this->SendData(friendUser, request, sizeof(AddFriendRequest));
}

/**
 * 处理添加好友结果。
 * @param result    添加好友结果
 */
void Server::HandleAddFriendResult(AddFriendResult* result) {
    sqlite3_stmt* query = NULL;
    CString sql;

    // 如果结果是同意，则将相关信息写到好友表中
    if (result->mResultCode == ADD_FRIEND_ALLOW) {
        sql.Format(_T("INSERT INTO %s VALUES('%s','%s')"), Const::TABLE_FRIENDS, result->mSrcUsername, result->mDstUsername);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
        sqlite3_step(query);
        sqlite3_finalize(query);

        sql.Format(_T("INSERT INTO %s VALUES('%s','%s')"), Const::TABLE_FRIENDS, result->mDstUsername, result->mSrcUsername);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
        sqlite3_step(query);
        sqlite3_finalize(query);
    }

    // 如果目标用户不在线，则将结果信息插入到数据库中
    Client* friendUser = NULL;
    BOOL found = mOnlineUsers.Lookup(result->mSrcUsername, friendUser);
    if (!found) {
        sql.Format(_T("INSERT INTO %s VALUES('%s','%s','%d')"), Const::TABLE_OFFLINE_ADD_FRIEND, result->mSrcUsername, result->mDstUsername, result->mResultCode);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
        sqlite3_step(query);
        sqlite3_finalize(query);
        return;
    }

    // 向目标用户发送结果
    this->SendData(friendUser, result, sizeof(AddFriendResult));
}

/**
 * 处理聊天。
 * @param chatInfo  聊天信息
 */
void Server::HandleChat(ChatInfo* chatInfo) {
    // 处理群发消息
    if (_tcscmp(chatInfo->mDstUsername, ImProtocol::MULTI_CHAT) == 0) {
        POSITION pos = mOnlineUsers.GetStartPosition();
        CString username;
        Client* client = NULL;
        while (pos != NULL) {
            mOnlineUsers.GetNextAssoc(pos, username, client);
            // 跳过自己，向其它用户发送聊天信息
            if (username != chatInfo->mSrcUsername) {
                this->SendData(client, chatInfo, sizeof(ChatInfo));
            }
        }
        return;
    }

    // 如果目标用户不在线，则将聊天信息插入到数据库中
    Client* friendUser = NULL;
    BOOL result = mOnlineUsers.Lookup(chatInfo->mDstUsername, friendUser);
    if (!result) {
        sqlite3_stmt* query = NULL;
        CString sql;
        sql.Format(_T("INSERT INTO %s VALUES('%s','%s','%s','%s')"), 
            Const::TABLE_OFFLINE_CHAT, chatInfo->mDstUsername, chatInfo->mSrcUsername, chatInfo->mContent, chatInfo->mTime);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
        sqlite3_step(query);
        sqlite3_finalize(query);
        return;
    }

    // 发送给目标用户
    this->SendData(friendUser, chatInfo, sizeof(ChatInfo));
}

/**
 * 处理离线请求。
 * @param request   离线请求
 */
void Server::HandleLogoff(LogoffRequest* request) {
    // 将该用户从在线列表移除
    mOnlineUsers.RemoveKey(request->mUsername);

    // 广播该用户的离线请求
    POSITION pos = mOnlineUsers.GetStartPosition();
    while (pos) {
        CString username;
        Client* client = NULL;
        mOnlineUsers.GetNextAssoc(pos, username, client);
        this->SendData(client, request, sizeof(LogoffRequest));
    }
}

/**
 * 处理登录请求。
 * @param request   登录请求
 * @param client    客户端
 */
void Server::HandleLogon(LogonRequest* request, Client* client) {
    LogonResult logonResult;
    sqlite3_stmt* query = NULL;
    CString sql;

    // 检查该用户是否已经登录
    BOOL result = mOnlineUsers.Lookup(request->mUserInfo.mUsername, client);
    if (result) {
        // 已经登录了
        logonResult.mResultCode = USER_ALREADY_LOGON;
        this->SendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 从数据库中查找该用户
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s'"), Const::TABLE_USERS, Const::FIELD_NAME, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    if (sqlite3_step(query) == SQLITE_DONE) {
        // 查无此人
        sqlite3_finalize(query);
        logonResult.mResultCode = USER_NOT_FOUND;
        this->SendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 比对密码
    LPCTSTR password;
#ifdef _UNICODE
    password = (LPCTSTR)sqlite3_column_text16(query, 1);
#else
    password = (LPCTSTR)sqlite3_column_text(query, 1);
#endif
    if (_tcscmp(password, request->mUserInfo.mPassword) != 0) {
        // 密码不正确
        sqlite3_finalize(query);
        logonResult.mResultCode = PASSWORD_WRONG;
        this->SendData(client, &logonResult, sizeof(logonResult));
        return;
    }

    // 登录成功，获取该用户的相关信息
    logonResult.mResultCode = LOGON_SUCCEEDED;
    logonResult.mIconIndex = sqlite3_column_int(query, 2);
    sqlite3_finalize(query);

    // 获取该用户的好友列表
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s'"), Const::TABLE_FRIENDS, Const::FIELD_OWNER, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);

    int friendCount = 0;
    LPCTSTR friendName;
    sqlite3_stmt* iconIndexQuery = NULL;
    BOOL isOnline = FALSE;

    for (friendCount = 0; sqlite3_step(query) == SQLITE_ROW; friendCount++) {
#ifdef _UNICODE
        friendName = (LPCTSTR)sqlite3_column_text16(query, 1);
#else
        friendName = (LPCTSTR)sqlite3_column_text(query, 1);
#endif
        _tcscpy_s(logonResult.mFriends[friendCount].mUsername, ImProtocol::USERNAME_BUF_LEN, friendName);

        sql.Format(_T("SELECT %s FROM %s WHERE %s='%s'"), Const::FIELD_ICON_INDEX, Const::TABLE_USERS, Const::FIELD_NAME, friendName);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &iconIndexQuery, NULL);
        sqlite3_step(iconIndexQuery);
        logonResult.mFriends[friendCount].mIconIndex = sqlite3_column_int(iconIndexQuery, 0);
        sqlite3_finalize(iconIndexQuery);

        Client* friendUser = NULL;
        isOnline = mOnlineUsers.Lookup(friendName, friendUser) ? TRUE: FALSE;
        logonResult.mFriendsOnline[friendCount] = isOnline;

        // 如果好友在线，则向好友发送通知
        if (isOnline) {
            FriendLogon friendLogon(request->mUserInfo.mUsername);
            this->SendData(friendUser, &friendLogon, sizeof(friendLogon));
        }
    }
    sqlite3_finalize(query);
    logonResult.mFriendCount = friendCount;

    // 发送登录结果
    this->SendData(client, &logonResult, sizeof(logonResult));

    // 查询是否有和该用户相关的离线添加好友请求，有则发送
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s' AND %s IS NULL"), 
        Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_DST_USER, request->mUserInfo.mUsername, Const::FIELD_RESULT);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    while (sqlite3_step(query) == SQLITE_ROW) {
        LPCTSTR srcUsername;
#ifdef _UNICODE
        srcUsername = (LPCTSTR)sqlite3_column_text16(query, 0);
#else
        srcUsername = (LPCTSTR)sqlite3_column_text(query, 0);
#endif
        sql.Format(_T("SELECT %s FROM %s WHERE %s='%s'"), Const::FIELD_ICON_INDEX, Const::TABLE_USERS, Const::FIELD_NAME, srcUsername);
        sqlite3_prepare16_v2(mDatabase, sql, -1, &iconIndexQuery, NULL);
        sqlite3_step(iconIndexQuery);

        AddFriendRequest addFriendRequest(request->mUserInfo.mUsername, sqlite3_column_int(iconIndexQuery, 0), srcUsername);
        this->SendData(client, &addFriendRequest, sizeof(addFriendRequest));
        sqlite3_finalize(iconIndexQuery);
    }
    sqlite3_finalize(query);
    
    // 删除数据库中的离线添加好友请求
    sql.Format(_T("DELETE FROM %s WHERE %s='%s'"), Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    // 查询是否有和该用户相关的离线添加好友结果，有则发送
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s' AND %s IS NOT NULL"), Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, request->mUserInfo.mUsername, Const::FIELD_RESULT);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    LPCTSTR dstUsername;
    while (sqlite3_step(query) == SQLITE_ROW) {
#ifdef _UNICODE
        dstUsername = (LPCTSTR)sqlite3_column_text16(query, 1);
#else
        dstUsername = (LPCTSTR)sqlite3_column_text(query, 1);
#endif
        ResultCode resultCode = (ResultCode)sqlite3_column_int(query, 2);
        AddFriendResult addFriendResult(-1, dstUsername, resultCode, request->mUserInfo.mUsername);
        this->SendData(client, &addFriendResult, sizeof(addFriendResult));
    }
    sqlite3_finalize(query);

    // 删除数据库中的离线添加好友结果
    sql.Format(_T("DELETE FROM %s WHERE %s='%s'"), Const::TABLE_OFFLINE_ADD_FRIEND, Const::FIELD_SRC_USER, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    // 查询是否有和该用户相关的离线聊天消息，有则发送
    sql.Format(_T("SELECT * FROM %s WHERE %s='%s'"), Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    LPCTSTR srcUsername;
    while (sqlite3_step(query) == SQLITE_ROW) {
#ifdef _UNICODE
        srcUsername = (LPCTSTR)sqlite3_column_text16(query, 1);
        CString content((LPCTSTR)sqlite3_column_text16(query, 2));
        CString time((LPCTSTR)sqlite3_column_text16(query, 3));
#else
        srcUsername = (LPCTSTR)sqlite3_column_text(query, 1);
        CString content((LPCTSTR)sqlite3_column_text(query, 2));
        CString time((LPCTSTR)sqlite3_column_text(query, 3));
#endif
        ChatInfo chatInfo(content, request->mUserInfo.mUsername, srcUsername, time);
        this->SendData(client, &chatInfo, sizeof(chatInfo));
    }
    sqlite3_finalize(query);

    // 删除数据库中的离线聊天信息
    sql.Format(_T("DELETE FROM %s WHERE %s='%s'"), Const::TABLE_OFFLINE_CHAT, Const::FIELD_DST_USER, request->mUserInfo.mUsername);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    sqlite3_step(query);
    sqlite3_finalize(query);

    // 将该用户加入在线列表
    mOnlineUsers.SetAt(request->mUserInfo.mUsername, client);
}

/**
 * 处理注册请求。
 * @param request   注册请求
 * @param client    客户端
 */
void Server::HandleReg(RegRequest* request, Client* client) {
    RegResult regResult;
    sqlite3_stmt* query = NULL;
    CString sql;

    // 将该用户信息插入到数据库中
    sql.Format(_T("INSERT INTO %s VALUES('%s','%s','%d')"), Const::TABLE_USERS,
        request->mUserInfo.mUsername, request->mUserInfo.mPassword, request->mUserInfo.mIconIndex);
    sqlite3_prepare16_v2(mDatabase, sql, -1, &query, NULL);
    if (sqlite3_step(query) != SQLITE_DONE) {
        // 该用户已存在
        sqlite3_finalize(query);
        regResult.mResultCode = USER_EXISTS;
        this->SendData(client, &regResult, sizeof(regResult));
        return;
    }
    sqlite3_finalize(query);

    // 发送结果
    regResult.mResultCode = REG_SUCCEEDED;
    this->SendData(client, &regResult, sizeof(regResult));

    // 将该用户加入到在线列表中
    mOnlineUsers.SetAt(request->mUserInfo.mUsername, client);
}

/**
 * 客户端连接服务器事件的响应方法。
 * @param errorCode 错误码
 */
void Server::OnAccept(int errorCode) {
    Client* client = new Client(this);
    CSocket::Accept(*client);

    CSocket::OnAccept(errorCode);
}

/**
 * 处理客户端发送来的数据。
 * @param pro       协议
 * @param client    客户端
 */
void Server::OnGetMessageFromClient(ImProtocol* pro, Client* client) {
    switch (pro->mType) {
        case ImProtocol::ADD_FRIEND_REQUEST:
            this->HandleAddFriendRequest((AddFriendRequest*)pro, client);
            break;
        case ImProtocol::ADD_FRIEND_RESULT:
            this->HandleAddFriendResult((AddFriendResult*)pro);
            break;
        case ImProtocol::CHAT:
            this->HandleChat((ChatInfo*)pro);
            break;
        case ImProtocol::LOGOFF:
            this->HandleLogoff((LogoffRequest*)pro);
            break;
        case ImProtocol::LOGON:
            this->HandleLogon((LogonRequest*)pro, client);
            break;
        case ImProtocol::REG:
            this->HandleReg((RegRequest*)pro, client);
            break;
    }
}

/**
 * 发送数据。
 * @param client    客户端
 * @param pro       协议
 * @param dataSize  数据长度
 */
void Server::SendData(Client* client, ImProtocol* pro, int dataSize) {
    // 发送数据长度
    client->Send(&dataSize, sizeof(dataSize));

    // 发送数据
    client->Send(pro, dataSize);

    // 防止发送过快导致客户端无法响应
    Sleep(100);
}

/**
 * 开启服务。
 * @return 成功则返回 TRUE，否则返回 FALSE
 */
BOOL Server::Start() {
    // 绑定端口
    BOOL result = CSocket::Create(ImProtocol::SERVER_PORT, SOCK_STREAM, ImProtocol::SERVER_IP);
    if (!result) {
        return result;
    }

    // 侦听
    return CSocket::Listen();
}
